package net.nampham.visioncontrol.view;

import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.ArrayList;

import android.opengl.GLES20;
import android.opengl.Matrix;
import android.util.Log;

import net.nampham.visioncontrol.Logger;
import net.nampham.visioncontrol.model.DColor;
import net.nampham.visioncontrol.model.IDebugger;
import net.nampham.visioncontrol.model.Vec2;

public class Renderer implements IDebugger {
	RObject lineList;
	RObject triList;
	
	private final int MAX_VERTEX = 1000; 
	private static final int FLOAT_SIZE_BYTES = 4;
	private static final int VERTEX_STRIDE = 7*FLOAT_SIZE_BYTES;
	
	
	public Renderer(){
		assert(the_singleton==null);
		the_singleton = this;
		
		lineList = new RObject();
		lineList.mat = new MatSingleColor();
		((MatSingleColor)lineList.mat).setColor(new RColor(1,0,0));
		
		triList = new RObject();
		triList.mat = lineList.mat;
		
		allocate_buffer();

		Matrix.setIdentityM(mMMatrix, 0);
		Logger.d("renderer created");
	}
	
	private void allocate_buffer(){
		lineList.vertices = ByteBuffer.allocateDirect(MAX_VERTEX*VERTEX_STRIDE)
			.order(ByteOrder.nativeOrder()).asFloatBuffer();
		lineList.primitive_count = 0;
		lineList.primitive_type = GLES20.GL_LINES;
		lineList.initialze();
		
		triList.vertices = ByteBuffer.allocateDirect(MAX_VERTEX*VERTEX_STRIDE)
			.order(ByteOrder.nativeOrder()).asFloatBuffer();
		triList.primitive_count = 0;
		triList.primitive_type = GLES20.GL_TRIANGLES;
		triList.initialze();		
	}
	
	public void render_line(Vec3 v0, Vec3 v1, RColor color){
		lineList.vertices.put(v0.v, 0, 3);
		lineList.vertices.put(color.v, 0, 4);

		lineList.vertices.put(v1.v, 0, 3);
		lineList.vertices.put(color.v, 0, 4);
		
		lineList.primitive_count++;
		assert(lineList.primitive_count<MAX_VERTEX);
	}
	
	public void render_triangle(Vec3 v0, Vec3 v1, Vec3 v2, RColor color){
		triList.vertices.put(v0.v, 0, 3);
		triList.vertices.put(color.v, 0, 4);

		triList.vertices.put(v1.v, 0, 3);
		triList.vertices.put(color.v, 0, 4);

		triList.vertices.put(v2.v, 0, 3);
		triList.vertices.put(color.v, 0, 4);

		triList.primitive_count++;
		assert(triList.primitive_count<MAX_VERTEX);
	}

	public Vec3 mEyePos = new Vec3(0,0,-12);
	public Vec3 mDirection = new Vec3(0,0,1);
	
	public void render(float time){
        GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        GLES20.glClear( GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);

        Vec3 target = mEyePos.add(mDirection);
        Matrix.setLookAtM(mVMatrix, 0, mEyePos.v[0], mEyePos.v[1], mEyePos.v[2], 
        		target.v[0], target.v[1], target.v[2], 0.0f, 1.0f, 0.0f);
        set_mvp_matrix();
        
		//draw primitives
		//set primitive material
		//draw all the primitive buffer
		GLES20.glDisable(GLES20.GL_BLEND);
		lineList.mat.initialize_common();
		lineList.render();
		lineList.primitive_count = 0;
		lineList.vertices.position(0);

		//TODO: move transparent object to other render queues
		GLES20.glEnable(GLES20.GL_BLEND);
		//GLES20.glBlendEquation(GLES20.GL_FUNC_ADD);//TODO: find out why this function returns error
		GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
		//GLES20.glBlendFuncSeparate(GLES20.GL_FUNC_ADD, GLES20.GL_FUNC_ADD);
		//triList.mat.initialize_common();//not necessary cause use the same material!
		triList.render();
		triList.primitive_count = 0;
		triList.vertices.position(0);

		
		//draw background/skybox
		
		//draw entity
		
		//draw transparent objects
	}
	
	public void resrouce_reset(int width, int height){
        GLES20.glViewport(0, 0, width, height);
        float ratio = (float) width / height;
        Matrix.frustumM(mProjMatrix, 0, -ratio, ratio, -1, 1, 3, 100);
	}

	public void renderLine(Vec2 v0, Vec2 v1, DColor color) {
		this.render_line(new Vec3(v0), new Vec3(v1), new RColor(color));
	}

	public void renderTriangle(Vec2 v0, Vec2 v1, Vec2 v2, DColor color) {
		this.render_triangle(new Vec3(v0), new Vec3(v1), new Vec3(v2), new RColor(color,0.3f));		
	}

	public void renderLine(net.nampham.visioncontrol.model3d.Vec3 v0,
			net.nampham.visioncontrol.model3d.Vec3 v1, DColor color) {
		// TODO Auto-generated method stub
		
	}
	
	private Vec2 offset1 = new Vec2(0.2f,0.2f);
	private Vec2 offset2 = new Vec2(0.2f, -0.2f);

	public void renderPoint(Vec2 point, DColor color){
		renderLine(point.dec(offset1), 
					point.add(offset1), color);
		renderLine(point.dec(offset2), 
					point.add(offset2), color);			
	}
	
    private void checkGlError(String op) {
        int error;
        while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
        	Logger.e(op + ": glError " + error);
            throw new RuntimeException(op + ": glError " + error);
        }
    }
	
    private static Renderer the_singleton;
    
    public static Renderer getSingleton(){
    	assert(the_singleton!=null);
    	return the_singleton;
    }
    
    private int cur_program = 0;
    
	public void use_program(int program){
		if (cur_program!=program){
			cur_program = program;
	        GLES20.glUseProgram(program);
	        checkGlError("glUseProgram");			
		}
	}
	
	private Buffer cur_buffer;
	public boolean set_current_buffer(Buffer new_buffer){
		if (cur_buffer!=new_buffer){
			cur_buffer = new_buffer;
			return true;
		}
		return false;
	}
	
	float[] mMMatrix = new float[16];
	float[] mVMatrix = new float[16];
	float[] mProjMatrix = new float[16];
	float[] mMVPMatrix = new float[16];

	private void set_mvp_matrix(){
        Matrix.multiplyMM(mMVPMatrix, 0, mVMatrix, 0, mMMatrix, 0);
        Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mMVPMatrix, 0);
	}
	
	public float[] get_mvp_matrix(){
		return mMVPMatrix;
	}
}
